<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //2.x
        //1.默认会递归，2.数组改变length是无效的，3.对象不存在的属性不能被拦截
        
        //为什么之前不考虑使用Proxy，
        //兼容性差，ie11不兼容

        let toProxy=new WeakMap()//弱引用映射表，es6.放置的是原对象：代理过的对象
        let toRow=new WeakMap()//被代理过的对象：原对象


        //判断是不是对象
        function isObject(val) {
            return typeof val === 'object' && val !== null
        }

        function hasOwn(target,key){
            return target.hasOwnProperty(key)
        }

        //1.响应式的核心方法
        function reactive(target) {
            //创建响应式对象
            return createReactiveObject(target)
        }

        //创建响应式对象的
        function createReactiveObject(target) {
            if (!isObject(target)) return
            //创建一个观察者
            let proxy=toProxy.get(target)
            if(proxy)return proxy//已经代理过了，直接将代理的结果返回
            if(toRow.has(target))return target//防止一个对象被多次代理
            let baseHandler = {
                get(target, key, receiver) {//target代表源对象{name:'zf'}，key代表name，receiver代表代理对象proxy
                    console.log('获取')
                    // return target[key]//vue3中就不这样写了
                    //proxy_reflect反射
                    let result = Reflect.get(target, key, receiver)
                    //proxy.age.n = 54，这里仔细看，实际上是先从代理对象中获取age属性，所以先走get，然后再给age中的n设置值，需要再将age对象进行代理
                    //不想vuex2.x默认就会递归，vue3.0在需要时候才递归

                    //收集依赖-订阅把当前的key和这个effect对应起来，只要key对应的值发生变化，就让effect执行
                    track(target,key)//如果目标上的这个key的值发生变化了，重新让数组中的effect执行即可
                    return isObject(result) ? reactive(result) : result
                    // return result

                },
                set(target, key, value, receiver) {
                    //怎么识别是改属性还是新增属性
                    let hadKey=hasOwn(target, key)//判断这个属性以前有没有
                    let oldValue=target[key]
                    let res = Reflect.set(target, key, value, receiver)//特点，有返回值，boolean
                    if(!hadKey){
                        console.log('新增属性')
                        trigger(target,'add',key)
                    }else if(oldValue !== value){
                        console.log('修改属性')
                        trigger(target,'set',key)
                    }//为了屏蔽无意义的修改
                    // target[key]=value//如果设置没成功，如果这个对象不可以被更改，
                   
                    return res
                },
                deleteProperty(target, key) {
                    console.log('删除')
                    let res = Reflect.deleteProperty(target, key)
                    return res
                }
            }
            let observed = new Proxy(target, baseHandler)
            toProxy.set(target,observed)//key是target原对象，value是observed被代理的对象，判断toProxy中有target原对象，直接取值就是代理对象了
            toRow.set(observed,target)//判断的时候key就是被代理的对象，直接返回
            return observed
        }
        //代理对象
        // let proxy=reactive({name:'zf'})
        // //也支持直接往数组里面加值，不像Vue2.x还要重写数组的方法
        // proxy.name='zs'

        // let proxy = reactive({ name: 'zf', age: { n: 2 },arr:[1,2,3] })//多层代理，通过get方法来判断
        //也支持直接往数组里面加值，不像Vue2.x还要重写数组的方法
        // proxy.age.n = 54
        // let proxy=reactive([1,2,3])
        // proxy.length=100
        // console.log(proxy.arr)
        // proxy.b=3//可以直接新增属性，新增的属性也会被代理，这是Vue2.x没有的
        // console.log(proxy)


        //依赖收集 === 发布订阅
        //栈  先进后出
        let activeEffectStacks=[]//栈型结构

        // {
        //     target:{
        //         key:[fn,fn]
        //     }
        // }
        let targetsMap=new WeakMap()

        function track(target,key){//如果这个target中的key变化了，我就执行数组里的方法
            let effect=activeEffectStacks[activeEffectStacks.length-1]
            if(effect){//有对应关系，才创建关联
                let depsMap=targetsMap.get(target)
                if(!depsMap){
                    targetsMap.set(target,depsMap=new Map())
                }
                let deps=depsMap.get(key)
                if(!deps){
                    depsMap.set(key,deps=new Set())
                }
                if(!deps.has(effect)){
                    deps.add(effect)
                }
            }
            //什么都不做

        }
        function trigger(target,type,key){
            let depsMap=targetsMap.get(target)
            if(depsMap){
                let deps=depsMap.get(key)
                if(deps){
                    deps.forEach(effect=>{
                        effect()
                    })
                }
            }
        }
        function effect(fn){
            //需要把fn这个函数变成响应式的函数，
            let effect=createReactiveEffect(fn)
            effect()//默认应该先执行一次
        }

        function createReactiveEffect(fn){
            let effect=function(){//这个就是创建的响应式的effect
                return run(effect,fn)//运行，1。让fn执行，第二个就是把这个effect存到栈中
            }
            return effect
        }
        function run(effect,fn){//运行fn，并且将effect存起来
            try{
                activeEffectStacks.push(effect)
            fn()
            }finally{
                activeEffectStacks.pop(effect)
            }
            
            
        }

        let obj1=reactive({name:'zf'})
        effect(()=>{//effect会执行两次，默认先执行一次，之后依赖的数据变化了，会再次执行
            console.log(obj1.name)//会调用get方法
        })
        obj1.name='jw'
    </script>
</body>

</html>